package admin_server

import (
	"encoding/json"
	"lark/client_agent"
	"lark/conf"
	"lark/server_agent"
	"net"
	"net/http"
	"net/http/pprof"
	"strings"
	"sync/atomic"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/spf13/cast"
	"github.com/zeast/logs"
)

func StartHTTP() {
	prometheus.MustRegister(NewStatCollector())

	var mux = http.NewServeMux()
	mux.HandleFunc("/settings", Settings)
	mux.HandleFunc("/jmtext_access_map", JmtextAccessMap)
	mux.HandleFunc("/thrift_access_map", ThriftAccessMap)
	mux.HandleFunc("/service_list", ServiceList)
	mux.HandleFunc("/backup_service_list", BackupServiceList)
	mux.Handle("/metrics", promhttp.Handler())
	mux.HandleFunc("/debug/pprof/", pprof.Index)
	mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
	mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	mux.HandleFunc("/debug/pprof/trace", pprof.Trace)

	var b = &http.Server{
		Addr: conf.HTTPAdminAddr,
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ip, err := getIP(r)
			if err != nil {
				logs.Error(err)
				Err(w, "非法访问")
				return
			}

			if !gwl.allow(ip) {
				logs.Errorf("非法的 IP:%s", ip)
				Err(w, "非法访问")
				return
			}

			mux.ServeHTTP(w, r)
		}),
	}

	go b.ListenAndServe()
}

func Settings(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		setSettings(w, r)
	} else if r.Method == "GET" {
		getSettings(w, r)
	}
}

var timeSettingsMap = map[string]func(time.Duration){
	"default_net_dial_timeout":  conf.SetDefaultNetDialTimeout,
	"default_net_read_timeout":  conf.SetDefaultNetReadTimeout,
	"default_net_write_timeout": conf.SetDefaultNetWriteTimeout,

	"client_agent_dial_remote_timeout":  conf.SetClientAgentDialRemoteTimeout,
	"client_agent_read_remote_timeout":  conf.SetClientAgentReadRemoteTimeout,
	"client_agent_write_remote_timeout": conf.SetClientAgentWriteRemoteTimeout,
	"client_agent_read_local_timeout":   conf.SetClientAgentReadLocalTimeout,
	"client_agent_write_local_timeout":  conf.SetClientAgentWriteLocalTimeout,

	"server_agent_read_remote_timeout":      conf.SetServerAgentReadRemoteTimeout,
	"server_agent_write_remote_timeout":     conf.SetServerAgentWriteRemoteTimeout,
	"server_agent_dial_local_timeout":       conf.SetServerAgentDialLocalTimeout,
	"server_agent_read_local_timeout":       conf.SetServerAgentReadLocalTimeout,
	"server_agent_write_local_timeout":      conf.SetServerAgentWriteLocalTimeout,
	"server_agent_request_max_waiting_time": conf.SetServerAgentRequestMaxWaitingTime,
}

var stringSettingsMap = map[string]func(s string){
	"client_agent_log_level": client_agent.SetLoggerLevel,
	"server_agent_log_level": server_agent.SetLoggerLevel,
}

var uint32SettingsMap = map[string]func(d uint32){
	"max_size_of_net_package": func(d uint32) { atomic.StoreUint32(&conf.MaxSizeOfNetPackage, d) },
}

func setSettings(w http.ResponseWriter, r *http.Request) {
	k := r.PostFormValue("key")
	v := r.PostFormValue("value")

	var err error
	var d time.Duration
	var s string
	var u32 uint32
	var have bool

	if f, ok := timeSettingsMap[k]; ok && err == nil {
		if d, err = cast.ToDurationE(v); err == nil {
			f(d)
			have = true
		}
	}

	if f, ok := stringSettingsMap[k]; ok && err == nil {
		if s, err = cast.ToStringE(v); err == nil {
			f(s)
			have = true
		}
	}

	if f, ok := uint32SettingsMap[k]; ok && err == nil {
		if u32, err = cast.ToUint32E(v); err == nil {
			f(u32)
			have = true
		}
	}

	if !have {
		Err(w, "没有这个功能")
	} else if err != nil {
		Err(w, err.Error())
	} else {
		Suc(w, "设置成功")
	}
}

func getSettings(w http.ResponseWriter, r *http.Request) {
	var m = map[string]string{
		"client_agent_log_level":    client_agent.LoggerLevel(),
		"server_agent_log_level":    server_agent.LoggerLevel(),
		"default_net_dial_timeout":  conf.DefaultNetDialTimeout().String(),
		"default_net_read_timeout":  conf.DefaultNetReadTimeout().String(),
		"default_net_write_timeout": conf.DefaultNetWriteTimeout().String(),

		"client_agent_dial_remote_timeout":  conf.ClientAgentDialRemoteTimeout().String(),
		"client_agent_read_remote_timeout":  conf.ClientAgentReadRemoteTimeout().String(),
		"client_agent_write_remote_timeout": conf.ClientAgentWriteRemoteTimeout().String(),
		"client_agent_read_local_timeout":   conf.ClientAgentReadLocalTimeout().String(),
		"client_agent_write_local_timeout":  conf.ClientAgentWriteLocalTimeout().String(),

		"server_agent_read_remote_timeout":      conf.ServerAgentReadRemoteTimeout().String(),
		"server_agent_write_remote_timeout":     conf.ServerAgentWriteRemoteTimeout().String(),
		"server_agent_dial_local_timeout":       conf.ServerAgentDialLocalTimeout().String(),
		"server_agent_read_local_timeout":       conf.ServerAgentReadLocalTimeout().String(),
		"server_agent_write_local_timeout":      conf.ServerAgentWriteLocalTimeout().String(),
		"server_agent_request_max_waiting_time": conf.ServerAgentRequestMaxWaitingTime().String(),

		"max_size_of_net_package": cast.ToString(conf.MaxSizeOfNetPackage),

		//以下是不能动态修改的参数
		"http_admin_addr":              conf.HTTPAdminAddr,
		"os_max_open_files":            cast.ToString(conf.OSMaxOpenFiles),
		"service_snapshot_path":        conf.ServiceSnapshotPath,
		"server_agent_request_backlog": cast.ToString(conf.ServerAgentRequestBacklog),
		"admin_whitelist":              strings.Join(conf.AdminWhitelist, ","),
	}

	Suc(w, m)
}

func JmtextAccessMap(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		server_agent.JmtextAccess.Range(func(key, value interface{}) bool {
			server_agent.JmtextAccess.Delete(key)
			return true
		})

		Suc(w, "已经清空")
		return
	}

	var m = make(map[string]interface{})

	server_agent.JmtextAccess.Range(func(key, value interface{}) bool {
		m[key.(string)] = value
		return true
	})

	Suc(w, m)
}

func ThriftAccessMap(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		server_agent.ThriftAccess.Range(func(key, value interface{}) bool {
			server_agent.ThriftAccess.Delete(key)
			return true
		})
		Suc(w, "已经清空")
		return
	}

	var m = make(map[string]interface{})

	server_agent.ThriftAccess.Range(func(key, value interface{}) bool {
		m[key.(string)] = value
		return true
	})

	Suc(w, m)
}

func ServiceList(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		Suc(w, client_agent.ServiceList())
	} else if r.Method == "POST" {
		err := client_agent.ResetServiceList()
		if err != nil {
			Err(w, err.Error())
		} else {
			Suc(w, "重置成功")
		}
	}
	return
}

func BackupServiceList(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		Suc(w, client_agent.BackupServiceList())
	}
	return
}

func Err(w http.ResponseWriter, msg string) {
	w.Header().Set("Content-Type", "application/json;charset=utf-8")

	w.WriteHeader(http.StatusBadRequest)

	w.Write([]byte(msg))
}

func Suc(w http.ResponseWriter, data interface{}) {
	w.Header().Set("Content-Type", "application/json;charset=utf-8")

	ret, _ := json.Marshal(data)

	w.Write(ret)
}

func getIP(r *http.Request) (string, error) {
	for _, key := range []string{
		"HTTP_CLIENT_IP",
		"HTTP_X_FORWARDED_FOR",
		"HTTP_X_FORWARDED",
		"HTTP_X_CLUSTER_CLIENT_IP",
		"HTTP_FORWARDED_FOR",
		"HTTP_FORWARDED",
	} {
		if ip := r.Header.Get(key); ip != "" {
			return ip, nil
		}
	}

	host, _, err := net.SplitHostPort(r.RemoteAddr)
	if err != nil {
		return "", err
	}

	return host, nil
}
